﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;

namespace CH.Easy.Aop
{
    public class ProxyFactory<T>
    {
        private static AssemblyBuilder assBuilder = null;
        private static ModuleBuilder modBuilder = null;
        private static readonly string DYNAMIC_PROXY_NAMESPACE = "CH.Easy.Aop.DynamicProxy";
        private static TypeBuilder typeBuilder = null;

        static ProxyFactory()
        {

            #region 代码原型
            //MethodInfo method = typeof(MemberLogic).GetMethod("UpdateMemberAge");
            //InvokeMessage invokeMessage = new InvokeMessage();
            //invokeMessage.Method = method;
            //invokeMessage.CanExcuteNext = true;
            //invokeMessage.SetParameter("age", age);
            //invokeMessage.SetParameter("uid", uid);

            //BeforeAspectAttribute[] befores = Attribute.GetCustomAttributes(method,typeof(BeforeAspectAttribute)).Cast<BeforeAspectAttribute>().ToArray<BeforeAspectAttribute>();
            //foreach (BeforeAspectAttribute before in befores)
            //{
            //    before.Excute(invokeMessage);
            //}
            //try
            //{
            //    if (invokeMessage.CanExcuteNext)
            //    {
            //        invokeMessage.Result = base.UpdateMemberAge(age, uid);
            //    }
            //    AfterAspectAttribute[] afters = Attribute.GetCustomAttributes(method, typeof(AfterAspectAttribute)).Cast<AfterAspectAttribute>().ToArray<AfterAspectAttribute>();
            //    foreach (AfterAspectAttribute after in afters)
            //    {
            //        after.Excute(invokeMessage);
            //    }
            //}
            //catch (Exception e)
            //{
            //    invokeMessage.Exception = e;
            //    ExceptionAspectAttribute[] excptions = Attribute.GetCustomAttributes(method, typeof(ExceptionAspectAttribute)).Cast<ExceptionAspectAttribute>().ToArray<ExceptionAspectAttribute>();
            //    foreach (ExceptionAspectAttribute excption in excptions)
            //    {
            //        excption.Excute(invokeMessage);
            //    }
            //}


            //return (int)invokeMessage.Result; 
            #endregion
            #region 初始化程序集
            Type realType = typeof(T);
            if (assBuilder == null)
            {
                //在当前应用域创建一个程序集
                AssemblyName assName = new AssemblyName(DYNAMIC_PROXY_NAMESPACE);
                assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
            }

            if (modBuilder == null)
            {
                //在程序集中创建模块
                modBuilder = assBuilder.DefineDynamicModule(DYNAMIC_PROXY_NAMESPACE, DYNAMIC_PROXY_NAMESPACE + ".dll");
            }

            if (typeBuilder == null)
            {
                //在模块中创建类型
                typeBuilder = modBuilder.DefineType(DYNAMIC_PROXY_NAMESPACE + "." + realType.Name + "Proxy", TypeAttributes.Public);
            }


            typeBuilder.SetParent(realType);

            //创建构造函数
            ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
            ILGenerator ctorIl = ctorBuilder.GetILGenerator();
            ctorIl.Emit(OpCodes.Ret);
            #endregion
            #region 创建方法
            MethodInfo[] methods = realType.GetMethods();
            foreach (MethodInfo method in methods)
            {

                if (!method.Attributes.HasFlag(MethodAttributes.Virtual) || method.Name == "ToString" || method.Name == "Equals" || method.Name == "GetHashCode")
                {
                    continue;
                }


                ParameterInfo[] parameters = method.GetParameters();
                MethodBuilder mb = typeBuilder.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, method.ReturnType, method.GetParameters().Select(p => p.ParameterType).ToArray());
                typeBuilder.DefineMethodOverride(mb, method);
                ILGenerator mbIl = mb.GetILGenerator();




                //定义一个局部变量 MethodInfo method;
                LocalBuilder methodBuilder = mbIl.DeclareLocal(typeof(MethodInfo));
                // typeof(T)
                //获取realType的元数据生成
                mbIl.Emit(OpCodes.Ldtoken, realType);
                //通过元数据获取运行时的类型 (存放在 加载堆 loader heap 中) 
                mbIl.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
                //调用方法typeof(T).GetMethod(method.Name);
                mbIl.Emit(OpCodes.Ldstr, method.Name);
                mbIl.Emit(OpCodes.Call, typeof(Type).GetMethod("GetMethod", new Type[] { typeof(string) }));
                //设置变量 method=typeof(T).GetMethod(method.Name);
                mbIl.Emit(OpCodes.Stloc, methodBuilder);
                //定义一个局部变量 InvokeMessage invokeMessage;
                LocalBuilder invokeMessageBuilder = mbIl.DeclareLocal(typeof(InvokeMessage));
                mbIl.Emit(OpCodes.Newobj, typeof(InvokeMessage).GetConstructor(Type.EmptyTypes));
                //设置变量 invokeMessage=new InvokeMessage();
                mbIl.Emit(OpCodes.Stloc, invokeMessageBuilder);
                //设置变量 invokeMessage.Method = method;
                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                mbIl.Emit(OpCodes.Ldloc, methodBuilder);
                mbIl.Emit(OpCodes.Call, typeof(InvokeMessage).GetMethod("set_Method"));
                //设置变量invokeMessage.CanExcuteNext = true;
                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                mbIl.Emit(OpCodes.Ldc_I4_1);
                mbIl.Emit(OpCodes.Call, typeof(InvokeMessage).GetMethod("set_CanExcuteNext"));
                //设置变量invokeMessage.SetParameter(arg); 注意参数OpCodes.Ldarg_0 =this
                for (int i = 0; i < parameters.Count(); i++)
                {
                    mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                    mbIl.Emit(OpCodes.Ldstr, parameters[i].Name);
                    mbIl.Emit(OpCodes.Ldarg, i + 1);
                    if (parameters[i].ParameterType.IsValueType)
                    {
                        mbIl.Emit(OpCodes.Box, parameters[i].ParameterType);
                    }

                    mbIl.Emit(OpCodes.Call, typeof(InvokeMessage).GetMethod("SetParameter"));
                }





                //BeforeAspectAttribute[] befores = Attribute.GetCustomAttributes(method,typeof(BeforeAspectAttribute)).Cast<BeforeAspectAttribute>().ToArray<BeforeAspectAttribute>();
                LocalBuilder beforesBuilder = mbIl.DeclareLocal(typeof(BeforeAspectAttribute[]));
                mbIl.Emit(OpCodes.Ldloc, methodBuilder);
                mbIl.Emit(OpCodes.Ldtoken, typeof(BeforeAspectAttribute));
                mbIl.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
                mbIl.Emit(OpCodes.Call, typeof(System.Attribute).GetMethod("GetCustomAttributes", new Type[] { typeof(System.Reflection.MethodInfo), typeof(System.Type) }));
                mbIl.Emit(OpCodes.Call, typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(typeof(BeforeAspectAttribute)));
                mbIl.Emit(OpCodes.Call, typeof(Enumerable).GetMethod("ToArray").MakeGenericMethod(typeof(BeforeAspectAttribute)));
                mbIl.Emit(OpCodes.Stloc, beforesBuilder);
                //foreach (BeforeAspectAttribute before in befores)
                //{
                //    before.Excute(invokeMessage);
                //}
                Label compareLabelForBefore = mbIl.DefineLabel();
                Label enterLoopLabelForBefore = mbIl.DefineLabel();
                //数组循环用临时变量
                LocalBuilder indexForBefore = mbIl.DeclareLocal(typeof(Int32));
                //index=0;
                mbIl.Emit(OpCodes.Ldc_I4_0);
                mbIl.Emit(OpCodes.Stloc, indexForBefore);
                //进入比较阶段
                mbIl.Emit(OpCodes.Br, compareLabelForBefore);
                mbIl.MarkLabel(enterLoopLabelForBefore);
                //befores[index].Excute(invokeMessage)
                //befores[index]
                mbIl.Emit(OpCodes.Ldloc, beforesBuilder);
                mbIl.Emit(OpCodes.Ldloc, indexForBefore);
                mbIl.Emit(OpCodes.Ldelem_Ref);
                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                //befores[index].Excute(invokeMessage)
                mbIl.Emit(OpCodes.Callvirt, typeof(BeforeAspectAttribute).GetMethod("Excute"));
                //index++
                mbIl.Emit(OpCodes.Ldloc, indexForBefore);
                mbIl.Emit(OpCodes.Ldc_I4_1);
                mbIl.Emit(OpCodes.Add);
                mbIl.Emit(OpCodes.Stloc, indexForBefore);
                //index<befores.count()
                mbIl.MarkLabel(compareLabelForBefore);
                mbIl.Emit(OpCodes.Ldloc, indexForBefore);
                mbIl.Emit(OpCodes.Ldloc, beforesBuilder);
                //把数组的长度 入栈
                mbIl.Emit(OpCodes.Ldlen);
                mbIl.Emit(OpCodes.Conv_I4);
                mbIl.Emit(OpCodes.Clt);
                mbIl.Emit(OpCodes.Brtrue_S, enterLoopLabelForBefore);










                //try
                //{
                //    if (invokeMessage.CanExcuteNext)
                //    {
                //        invokeMessage.Result = base.UpdateMemberAge(age, uid);
                //    }
                //    AfterAspectAttribute[] afters = method.GetCustomAttributes(typeof(AfterAspectAttribute), false).Cast<AfterAspectAttribute>().ToArray<AfterAspectAttribute>();
                //    foreach (AfterAspectAttribute after in afters)
                //    {
                //        after.Excute(invokeMessage);
                //    }
                //}
                //try
                mbIl.BeginExceptionBlock();
                Label falseLabel = mbIl.DefineLabel();
                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                mbIl.Emit(OpCodes.Call, typeof(InvokeMessage).GetMethod("get_CanExcuteNext"));
                mbIl.Emit(OpCodes.Ldc_I4_1);
                mbIl.Emit(OpCodes.Ceq);
                mbIl.Emit(OpCodes.Brfalse, falseLabel);
                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                mbIl.Emit(OpCodes.Ldarg_0);
                for (int i = 0; i < parameters.Count(); i++)
                {
                    mbIl.Emit(OpCodes.Ldarg, i + 1);
                }
                mbIl.Emit(OpCodes.Call, method);
                mbIl.Emit(OpCodes.Call, typeof(InvokeMessage).GetMethod("set_Result"));
                mbIl.MarkLabel(falseLabel);


                //AfterAspectAttribute[] befores = Attribute.GetCustomAttributes(method,typeof(AfterAspectAttribute)).Cast<AfterAspectAttribute>().ToArray<AfterAspectAttribute>();
                LocalBuilder aftersBuilder = mbIl.DeclareLocal(typeof(AfterAspectAttribute[]));
                mbIl.Emit(OpCodes.Ldloc, methodBuilder);
                mbIl.Emit(OpCodes.Ldtoken, typeof(AfterAspectAttribute));
                mbIl.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
                mbIl.Emit(OpCodes.Call, typeof(System.Attribute).GetMethod("GetCustomAttributes", new Type[] { typeof(System.Reflection.MethodInfo), typeof(System.Type) }));
                mbIl.Emit(OpCodes.Call, typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(typeof(AfterAspectAttribute)));
                mbIl.Emit(OpCodes.Call, typeof(Enumerable).GetMethod("ToArray").MakeGenericMethod(typeof(AfterAspectAttribute)));
                mbIl.Emit(OpCodes.Stloc, aftersBuilder);
                //foreach (AfterAspectAttribute after in afters)
                //{
                //    after.Excute(invokeMessage);
                //}
                Label compareLabelForAfter = mbIl.DefineLabel();
                Label enterLoopLabelForAfter = mbIl.DefineLabel();
                //数组循环用临时变量
                LocalBuilder indexForAfter = mbIl.DeclareLocal(typeof(Int32));
                //index=0;
                mbIl.Emit(OpCodes.Ldc_I4_0);
                mbIl.Emit(OpCodes.Stloc, indexForAfter);
                //进入比较阶段
                mbIl.Emit(OpCodes.Br, compareLabelForAfter);
                mbIl.MarkLabel(enterLoopLabelForAfter);
                //afters[index].Excute(invokeMessage)
                //afters[index]
                mbIl.Emit(OpCodes.Ldloc, aftersBuilder);
                mbIl.Emit(OpCodes.Ldloc, indexForAfter);
                mbIl.Emit(OpCodes.Ldelem_Ref);
                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                //afters[index].Excute(invokeMessage)
                mbIl.Emit(OpCodes.Callvirt, typeof(AfterAspectAttribute).GetMethod("Excute"));
                //index++
                mbIl.Emit(OpCodes.Ldloc, indexForAfter);
                mbIl.Emit(OpCodes.Ldc_I4_1);
                mbIl.Emit(OpCodes.Add);
                mbIl.Emit(OpCodes.Stloc, indexForAfter);
                //index<afters.count()
                mbIl.MarkLabel(compareLabelForAfter);
                mbIl.Emit(OpCodes.Ldloc, indexForAfter);
                mbIl.Emit(OpCodes.Ldloc, aftersBuilder);
                //把数组的长度 入栈
                mbIl.Emit(OpCodes.Ldlen);
                mbIl.Emit(OpCodes.Conv_I4);
                mbIl.Emit(OpCodes.Clt);
                mbIl.Emit(OpCodes.Brtrue_S, enterLoopLabelForAfter);







                //catch 注意，这个时侯堆栈顶为异常信息ex
                mbIl.BeginCatchBlock(typeof(Exception));
                //invokeMessage.Exception = e;
                LocalBuilder exBuilder = mbIl.DeclareLocal(typeof(Exception));
                mbIl.Emit(OpCodes.Stloc, exBuilder);
                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                mbIl.Emit(OpCodes.Ldloc, exBuilder);
                mbIl.Emit(OpCodes.Call, typeof(InvokeMessage).GetMethod("set_Exception"));



                //ExceptionAspectAttribute[] befores = Attribute.GetCustomAttributes(method,typeof(ExceptionAspectAttribute)).Cast<ExceptionAspectAttribute>().ToArray<ExceptionAspectAttribute>();
                LocalBuilder exceptionsBuilder = mbIl.DeclareLocal(typeof(ExceptionAspectAttribute[]));
                mbIl.Emit(OpCodes.Ldloc, methodBuilder);
                mbIl.Emit(OpCodes.Ldtoken, typeof(ExceptionAspectAttribute));
                mbIl.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
                mbIl.Emit(OpCodes.Call, typeof(System.Attribute).GetMethod("GetCustomAttributes", new Type[] { typeof(System.Reflection.MethodInfo), typeof(System.Type) }));
                mbIl.Emit(OpCodes.Call, typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(typeof(ExceptionAspectAttribute)));
                mbIl.Emit(OpCodes.Call, typeof(Enumerable).GetMethod("ToArray").MakeGenericMethod(typeof(ExceptionAspectAttribute)));
                mbIl.Emit(OpCodes.Stloc, exceptionsBuilder);
                //foreach (ExceptionAspectAttribute excption in excptions)
                //{
                //    excption.Excute(invokeMessage);
                //}
                Label compareLabelForException = mbIl.DefineLabel();
                Label enterLoopLabelForException = mbIl.DefineLabel();
                //数组循环用临时变量
                LocalBuilder indexForException = mbIl.DeclareLocal(typeof(Int32));
                //index=0;
                mbIl.Emit(OpCodes.Ldc_I4_0);
                mbIl.Emit(OpCodes.Stloc, indexForException);
                //进入比较阶段
                mbIl.Emit(OpCodes.Br, compareLabelForException);
                mbIl.MarkLabel(enterLoopLabelForException);
                //excptions[index].Excute(invokeMessage)
                mbIl.Emit(OpCodes.Ldloc, exceptionsBuilder);
                mbIl.Emit(OpCodes.Ldloc, indexForException);
                mbIl.Emit(OpCodes.Ldelem_Ref);
                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                mbIl.Emit(OpCodes.Callvirt, typeof(ExceptionAspectAttribute).GetMethod("Excute"));
                //index++
                mbIl.Emit(OpCodes.Ldloc, indexForException);
                mbIl.Emit(OpCodes.Ldc_I4_1);
                mbIl.Emit(OpCodes.Add);
                mbIl.Emit(OpCodes.Stloc, indexForException);
                //index<excptions.count()
                mbIl.MarkLabel(compareLabelForException);
                mbIl.Emit(OpCodes.Ldloc, indexForException);
                mbIl.Emit(OpCodes.Ldloc, exceptionsBuilder);
                //把数组的长度 入栈
                mbIl.Emit(OpCodes.Ldlen);
                mbIl.Emit(OpCodes.Conv_I4);
                mbIl.Emit(OpCodes.Clt);
                mbIl.Emit(OpCodes.Brtrue_S, enterLoopLabelForException);
                //捕获结束
                mbIl.EndExceptionBlock();




















                mbIl.Emit(OpCodes.Ldloc, invokeMessageBuilder);
                mbIl.Emit(OpCodes.Call, typeof(InvokeMessage).GetMethod("get_Result"));
                mbIl.Emit(OpCodes.Ret);

            } 
            #endregion
        }


        public static T Create()
        {
            return (T)Activator.CreateInstance(typeBuilder.CreateType());
        }
        public static T CreateAndSave()
        {
            Type proxyType = typeBuilder.CreateType();
            assBuilder.Save(DYNAMIC_PROXY_NAMESPACE + ".dll");
            return (T)Activator.CreateInstance(proxyType);
        }
    }
}
